#pragma once
#include <iostream>
#include <queue>
template <class T>
class BlockQueue
{
    static const int defaultnum=20;
    private:
    int high_water;
    int low_water;
    int Maxcapa_;//最大容量
    int Mincapa_;//最小容量
    std::queue<T> blockqueue_;//队列容器
    pthread_mutex_t mutex_;//锁（访问队列这个共享资源的数据时为了解决并发的问题要上锁）
    pthread_cond_t p_cond_;//生产者到生产者的条件变量的队列里面等//条件变量，为了解决互斥带来的线程饥饿问题，在保证数据安全的前提下让线程有序进行访问
    pthread_cond_t  c_cond_;//消费者到消费者的条件变量的队列里面等
    public:
    BlockQueue(int Maxcapa=defaultnum)
    {
        Mincapa_=0;
        Maxcapa_=defaultnum;
        high_water=(Maxcapa_*2)/3;
        low_water=Maxcapa_*1/3;
        //构造的时候把锁和条件变量初始化好
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&p_cond_,nullptr);
        pthread_cond_init(&c_cond_,nullptr);
    }
    ~BlockQueue()
    {
        //析构时把锁和条件变量销毁
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&p_cond_);
        pthread_cond_destroy(&c_cond_);
    }
    T pop()
    {
        //访问共享资源前先上锁
        pthread_mutex_lock(&mutex_);
        //在保证数据安全的前提下有序访问-- 同步
        while(blockqueue_.size()==0)//没有数据可消费的时候，去条件变量里面等待唤醒
        {//避免伪唤醒用while循环
        pthread_cond_wait(&c_cond_,&mutex_);//等待并解锁
        }
        T out=blockqueue_.front();
        blockqueue_.pop();
        if(blockqueue_.size()<low_water)pthread_cond_signal(&p_cond_);//小于低水位可以唤醒一次生产者队列
        pthread_mutex_unlock(&mutex_);//解锁
        return out;
    }
    void push(const T& data)
    {
        pthread_mutex_lock(&mutex_);
        while(blockqueue_.size()==Maxcapa_)
        {
            //如果队列容量满了就等待
            pthread_cond_wait(&p_cond_,&mutex_);
        }
        blockqueue_.push(data);
        if(blockqueue_.size()>high_water)pthread_cond_signal(&c_cond_);//如果生产的数据高于高水位就可以唤醒消费者队列来消费
        pthread_mutex_unlock(&mutex_);
    }
};